//
//	AT91 HARDWARE SPI
//
//  (c) 2008 mocom software GmbH & Co KG
//	for European Microsoft Innovation Center
//
//  $Author: volker $
//  $Date: 2009-10-17 13:41:36 +0200 (Sa, 17. Okt 2009) $
//  $Revision: 522 $
//
//  Microsoft dotNetMF Project
//  Copyright ©2001,2002,2003,2004 Microsoft Corporation
//  One Microsoft Way, Redmond, Washington 98052-6399 U.S.A.
//  All rights reserved.
//  MICROSOFT CONFIDENTIAL
//
//-----------------------------------------------------------------------------
//
//	CLR does not handle this through PAL/APC.
//
//	"The behavior of fast drivers that transfer only small amounts of data at a
//	 time is different. Such drivers are treated synchronously by the CLR. An
//	 example of this category of drivers is the serial peripheral interface
//	 (SPI) driver. "
//
//	  		from .NET MF Documentation.
//
//-----------------------------------------------------------------------------
//
#include <tinyhal.h>
#include <Cores\arm\Include\cpu.h>
//#include "at91_spi.h"

//--//

#undef  TRACE_ALWAYS

#define TRACE_ALWAYS               0x00000001

#undef  DEBUG_TRACE
#define DEBUG_TRACE (TRACE_ALWAYS)

/***************************************************************************/

#if defined(ADS_LINKER_BUG__NOT_ALL_UNUSED_VARIABLES_ARE_REMOVED)
#pragma arm section zidata = "g_AT91_SPI_Driver"
#endif

AT91_SPI_Driver g_AT91_SPI_Driver;

#if defined(ADS_LINKER_BUG__NOT_ALL_UNUSED_VARIABLES_ARE_REMOVED)
#pragma arm section zidata
#endif

//--//

#define DEBUG_SPI  0
#if DEBUG_SPI == 3
#define SPI_DEBUG_PRINT  			hal_printf
#define SPI_DUMP_REGS(x)
#define SPI_PRINT_REG(x)			hal_printf( #x "=%8.8lx\r\n",x)
#define SPI_DUMP_BUFFER(m,b,l,s)
#elif DEBUG_SPI == 2
#define SPI_DEBUG_PRINT  			hal_printf
#define SPI_DUMP_REGS(x)			spi_dump_regs(x)
#define SPI_PRINT_REG(x)			hal_printf( #x "=%8.8lx\r\n",x)
#define SPI_DUMP_BUFFER(m,b,l,s)	msac_dbg_dump(m,b,l,s)
#elif DEBUG_SPI == 1
#define SPI_DEBUG_PRINT  			hal_printf
#define SPI_DUMP_REGS(x)
#define SPI_PRINT_REG(x)			hal_printf( #x "=%8.8lx\r\n",x)
#define SPI_DUMP_BUFFER(m,b,l,s)	msac_dbg_dump(m,b,l,s)
#else
#define SPI_DEBUG_PRINT				nul
#define SPI_DUMP_REGS(x)
#define SPI_PRINT_REG(x)
#define SPI_DUMP_BUFFER(m,b,l,s)
#endif

#define SET_INPUT			0
#define SET_OUTPUT			(!SET_INPUT)

// Pointer to Hardware
static AT91_SPI * const g_spi[AT91_SPI::c_MAX_SPI] = {(AT91_SPI *)AT91C_BASE_SPI0,(AT91_SPI *)AT91C_BASE_SPI1};

// convert 0 1 2 3 to Chipselect-Const
static const UINT32 pcsmap[4] = {0xe,0xd,0xb,0x7};

// hold off times - works ONLY if the caller controlls CS. 
// in the HAG project this is true for the sdcard driver only.
static UINT32 holdoff[2];

// dma receive buffer
#define SIZE_OF_TRASHCAN	1024	//256
static UINT8 trashcan[SIZE_OF_TRASHCAN];
static UINT8 nullcan[SIZE_OF_TRASHCAN];

// MMU Pointer
static UINT32* const c_Bootstrap_BaseOfTTBs     =   (UINT32*)((SRAM1_MEMORY_Base + SRAM1_MEMORY_Size) - ARM9_MMU::c_TTB_size);

// need to map logicla to physical for PDMA
void *MMU_LogicalToPhysical(void *laddr)
{
	UINT32* entry;

	entry = ARM9_MMU::GetL1Entry( c_Bootstrap_BaseOfTTBs, (UINT32) laddr );
	if (entry && *entry) 
	{
		return (void *) ( (((UINT32) laddr) & 0x000FFFFF) | (*entry & 0xFFF00000) );
	}
	return laddr;
}

//---------------------------//

static inline void nul(const char *fmt, ...) {}
#if DEBUG_SPI
void msac_dbg_dump(char *msg, void *buffer, unsigned len, unsigned size)
{
	unsigned i, max;
	UINT32 *nf32 = (UINT32*) buffer;
	UINT16 *nf16 = (UINT16*) buffer;
	UINT8 *nf8  = (UINT8*) buffer;

	hal_printf("%s: ",msg);

	if (len > 12)
		max = 12;
	else
		max = len;

	for (i=0;i<max;i++) {
		if (size == 4)
			hal_printf("%8.8x ",*nf32++);
		else if (size == 2)
			hal_printf("%4.4x ",*nf16++&0xffff);
		else {
			hal_printf("%2.2x ",*nf8++&0xff);
		}
	}

	if (max < len)
		hal_printf("...");

	hal_printf("\r\n");
}

static void msac_dbg_dump_pdma(AT91_SPI *spi)
{
	SPI_PRINT_REG(spi->RPR);     	// (256) Receive Pointer Register
	SPI_PRINT_REG(spi->RCR);     	// (260) Receive Counter Register
	SPI_PRINT_REG(spi->TPR);     	// (264) Transmit Pointer Register
	SPI_PRINT_REG(spi->TCR);     	// (268) Transmit Counter Register
	SPI_PRINT_REG(spi->NRPR);    	// (272) Receive Next Pointer Register
	SPI_PRINT_REG(spi->NRCR);    	// (276) Receive Next Counter Register
	SPI_PRINT_REG(spi->NTPR);    	// (280) Transmit Next Pointer Register
	SPI_PRINT_REG(spi->NTCR);    	// (284) Transmit Next Counter Register
	SPI_PRINT_REG(spi->PTCR);    	// (288) PDC Transfer Control Register
	SPI_PRINT_REG(spi->PTSR);    	// (292) PDC Transfer Status Register
}

static void spi_dump_regs(AT91_SPI *spi)
{
	SPI_PRINT_REG(spi->SPI_CR);         // ( 0) Control Register
	SPI_PRINT_REG(spi->SPI_MR);      	// ( 4) Mode Register
	SPI_PRINT_REG(spi->SPI_SR);      	// (16) Status Register
	SPI_PRINT_REG(spi->CSR[0]);    	// (48) Chip Select Register
	SPI_PRINT_REG(spi->CSR[1]);    	// (48) Chip Select Register
	SPI_PRINT_REG(spi->CSR[2]);    	// (48) Chip Select Register
	SPI_PRINT_REG(spi->CSR[3]);    	// (48) Chip Select Register
	msac_dbg_dump_pdma(spi);
}
#endif

static inline int is_spi_busy(AT91_SPI *spi)
{
	// fix: 24.03.09 VG
	// use AT91C_SPI_RXBUFF & AT91C_SPI_TXBUFE
	if (spi->SPI_SR & AT91_SPI::SPI_SR_RXBUFF && spi->SPI_SR & AT91_SPI::SPI_SR_TXBUFE) {
		//--SPI_PRINT_REG(spi->SPI_SR);      	// (16) Status Register
		spi->PTCR = AT91_SPI::PDC_RXTDIS | AT91_SPI::PDC_TXTDIS;	/* PDC Disable Rx,Tx */
		return FALSE;
	}
	return TRUE;
}

BOOL AT91_SPI_Driver::Initialize()
{
	static int once = 0;

	if (once == 0xaa55) {
		//SPI_DEBUG_PRINT("CPU_SPI_Initialize() -- ignored\r\n");
		return TRUE;
	}

	once = 0xaa55;
	//SPI_DEBUG_PRINT("CPU_SPI_Initialize()\r\n");


    AT91_PMC &pmc = AT91::PMC();
    pmc.EnablePeriphClock(AT91C_ID_SPI0);
    pmc.EnablePeriphClock(AT91C_ID_SPI1);

	// needs to be all 0
	memset(nullcan,0,sizeof(nullcan));

	for (int i=0;i<AT91_SPI::c_MAX_SPI;i++) {
		g_spi[i]->SPI_CR = AT91_SPI::SPI_CR_SW_RESET;
	}

	// SPI0 MISO, MOSI, SPCK
	CPU_GPIO_DisablePin(AT91_GPIO_Driver::PA0,RESISTOR_PULLUP,SET_INPUT,GPIO_ALT_MODE_1);
	CPU_GPIO_DisablePin(AT91_GPIO_Driver::PA1,RESISTOR_PULLUP,SET_OUTPUT,GPIO_ALT_MODE_1);
	CPU_GPIO_DisablePin(AT91_GPIO_Driver::PA2,RESISTOR_PULLUP,SET_OUTPUT,GPIO_ALT_MODE_1);

	// SPI1 MISO, MOSI, SPCK
	CPU_GPIO_DisablePin(AT91_GPIO_Driver::PB30,RESISTOR_PULLUP,SET_INPUT,GPIO_ALT_MODE_1);
	CPU_GPIO_DisablePin(AT91_GPIO_Driver::PB31,RESISTOR_PULLUP,SET_OUTPUT,GPIO_ALT_MODE_1);
	CPU_GPIO_DisablePin(AT91_GPIO_Driver::PB29,RESISTOR_PULLUP,SET_OUTPUT,GPIO_ALT_MODE_1);

	// chipselects SPI0 0,1,2,3
	CPU_GPIO_DisablePin(AT91_SPI0_CS0,RESISTOR_PULLUP,SET_OUTPUT,GPIO_ALT_MODE_1);
	CPU_GPIO_DisablePin(AT91_SPI0_CS1,RESISTOR_PULLUP,SET_OUTPUT,GPIO_ALT_MODE_1);
	CPU_GPIO_DisablePin(AT91_SPI0_CS2,RESISTOR_PULLUP,SET_OUTPUT,GPIO_ALT_MODE_1);
	//CPU_GPIO_DisablePin(AT91_SPI0_CS3,RESISTOR_PULLUP,SET_OUTPUT,GPIO_ALT_MODE_1);

	// chipselects SPI1 2,3
	//CPU_GPIO_DisablePin(AT91_SPI1_CS2,RESISTOR_PULLUP,TRUE,GPIO_ALT_MODE_2);
	CPU_GPIO_DisablePin(AT91_SPI1_CS3,RESISTOR_PULLUP,SET_OUTPUT,GPIO_ALT_MODE_2);

	for (int i=0;i<AT91_SPI::c_MAX_SPI;i++) {
		g_spi[i]->SPI_MR = (((-1)<<AT91_SPI::SPI_MR_PCS_SHIFT)&AT91_SPI::SPI_MR_PCS_MASK) + AT91_SPI::SPI_MR_MSTR;
		//--SPI_PRINT_REG(g_spi[i]->SPI_MR);
		g_AT91_SPI_Driver.m_Enabled[i] = TRUE;
		// enable
		g_spi[i]->SPI_CR = AT91_SPI::SPI_CR_ENABLE_SPI;
		//--SPI_PRINT_REG(g_spi[i]->SPI_SR);
		//--SPI_DEBUG_PRINT("Enable SPI%d\r\n",i);

	}

	return TRUE;
}


void AT91_SPI_Driver::Uninitialize()
{
	/*
	for (int i=0;i<AT91_SPI::c_MAX_SPI;i++) {
		//--SPI_DEBUG_PRINT("DISABLE SPI%d\r\n",i);
		g_spi[i]->SPI_MR = AT91C_SPI_PCS(-1);
		g_spi[i]->SPI_CR = AT91C_SPI_SPIDIS;
		g_AT91_SPI_Driver.m_Enabled[i] = FALSE;
	}

	AT91_PMC_DisablePeriphClock(AT91C_ID_SPI0);
	AT91_PMC_DisablePeriphClock(AT91C_ID_SPI1);
	*/
}

void AT91_SPI_Driver::GetPins(UINT32 spi_mod, GPIO_PIN &msk, GPIO_PIN &miso, GPIO_PIN &mosi)
{
  NATIVE_PROFILE_HAL_PROCESSOR_SPI();

  switch(spi_mod)
  {
    case 0:
      msk = AT91_SPI0_SCLK;
      miso = AT91_SPI0_MISO;
      mosi = AT91_SPI0_MOSI;
      break;
    case 1:
      msk = AT91_SPI1_SCLK;
      miso = AT91_SPI1_MISO;
      mosi = AT91_SPI1_MOSI;
      break;
    default:
      break;

  }
}

/***************************************************************************/


static inline BOOL spi_transfer(AT91_SPI *spi,
				  void* Write,
				  INT32 WriteCount,
				  void* Read,
				  INT32 ReadCount,
				  INT32 ReadStartOffset,
				  int size)
{
	ASSERT(size == 2 || size == 1);

	UINT32 fillup=0,read=0;
	int trash=0,ack=TRUE;

	if (WriteCount == 0){
		SPI_DEBUG_PRINT("WriteCount == 0, FAIL\r\n");
		return FALSE;
	}

	//--SPI_DEBUG_PRINT("SPI ReadStartOffset=%d ReadCount=%d WriteCount=%d\r\n",ReadStartOffset,ReadCount,WriteCount);

	spi->PTCR = AT91_SPI::PDC_RXTDIS | AT91_SPI::PDC_TXTDIS;  /* PDC Disable Rx/Tx */

	SPI_DUMP_BUFFER("WR",Write,WriteCount,size);

	// preset PDC
	spi->TPR  = MMU_LogicalToPhysical(Write);
	spi->TCR  = WriteCount;
	spi->NTPR = NULL;
	spi->NTCR = 0;
	spi->NRPR = NULL;
	spi->NRCR = 0;

	if (ReadStartOffset == 0 && ReadCount == WriteCount) {
		// plain
		//--SPI_DEBUG_PRINT("SPI: ReadStartOffset == 0 && ReadCount == WriteCount!\r\n");
		spi->RPR  = MMU_LogicalToPhysical(Read);
		spi->RCR  = read = ReadCount;
	} else if (ReadStartOffset == 0 && ReadCount == 0) {
		trash = WriteCount;ack=TRUE;
		if (WriteCount < sizeof(trashcan)/size) {
			//--SPI_DEBUG_PRINT("SPI: receive to trashcan\r\n");
			spi->RPR  = MMU_LogicalToPhysical(trashcan);
			spi->RCR  = WriteCount;
		} else if (WriteCount/2 < sizeof(trashcan)/size) {
			//--SPI_DEBUG_PRINT("SPI: receive to trashcan twice!\r\n");
			spi->RPR  = MMU_LogicalToPhysical(trashcan);
			spi->RCR  = WriteCount/2;
			spi->NRPR = MMU_LogicalToPhysical(trashcan);
			spi->NRCR = WriteCount/2;
		} else {
			SPI_DEBUG_PRINT("SPI: trashcan TOO SMALL, FAIL\r\n");
			return FALSE;
		}
	} else if (ReadStartOffset == 0 && ReadCount && ReadCount > WriteCount) {
		spi->RPR = MMU_LogicalToPhysical(Read);
		spi->RCR = read = ReadCount;
		fillup = ReadCount - WriteCount;
		if (fillup > sizeof(nullcan)) {
			//--SPI_DEBUG_PRINT("SPI: NULLCAN TOO SMALL, FAIL\r\n");
			return FALSE;
		}
		spi->NTPR = MMU_LogicalToPhysical(nullcan);					/* PDC Set Next Tx */
		spi->NTCR = fillup;
	} else if (ReadStartOffset) {
		//--SPI_DEBUG_PRINT("SPI: ReadStartOffset=%d\r\n",ReadStartOffset);
		trash = ReadStartOffset;
		if (ReadStartOffset < (sizeof(trashcan)/size)) {
			//--SPI_DEBUG_PRINT("SPI: ReadStartOffset < trashcan\r\n");
			// put ReadStartOffset bytes in trashcan
			spi->RPR  = MMU_LogicalToPhysical(trashcan);				/* PDC Set Rx */
			spi->RCR  = ReadStartOffset;
			if (ReadCount) {
				//--SPI_DEBUG_PRINT("SPI: ReadCount = %d\r\n",ReadCount);
				spi->NRPR = MMU_LogicalToPhysical(Read);
				spi->NRCR = ReadCount;
			}
			if (ReadStartOffset + ReadCount > WriteCount) {
				//--SPI_DEBUG_PRINT("SPI: ReadStartOffset + ReadCount > WriteCount\r\n");
				fillup = (ReadStartOffset+ReadCount) - WriteCount;
				if (fillup > sizeof(nullcan)) {
					//--SPI_DEBUG_PRINT("SPI: NULLCAN TOO SMALL, FAIL\r\n");
					return FALSE;
				}
				spi->NTPR = MMU_LogicalToPhysical(nullcan);					/* PDC Set Next Tx */
				spi->NTCR = fillup;
			}
		} else {
			SPI_DEBUG_PRINT("SPI: TRASHCAN TOO SMALL, FAIL\r\n");
			// if ReadCount+ReadStartOffset < WriteCount and ReadStartOffset != 0 or
			// or trashcan too small, we have to surrender
			return FALSE;
		}
	} else {
		SPI_DEBUG_PRINT("SPI: FAIL ReadStartOffset=%d ReadCount=%d WriteCount=%d\r\n",ReadStartOffset,ReadCount,WriteCount);
		return FALSE;
	}

	// make sure the data we want to sent is in memory, not only dcache	
	CPU_MakeDCacheCoherent((size_t)Write,WriteCount*size);

	// enable SPI/PDC
	spi->PTCR = AT91_SPI::PDC_RXTEN | AT91_SPI::PDC_TXTEN;		/* PDC Enable Rx/Tx */

	// wait for completion if required
	if (ack) {
		while (is_spi_busy(spi)) {
			// make sure tasking is driven while we wait
			HAL_CONTINUATION::Dequeue_And_Execute();
		}
	}

	// make sure CPU will fetch from memory, not from DCACHE
	CPU_DCacheInvalidate((size_t)Read,(ReadCount)*size);
	CPU_DCacheInvalidate((size_t)trashcan,trash);

	if (trash && spi == g_spi[1]) {
		SPI_DUMP_BUFFER("SPI1*:",trashcan,trash,size);
	}
	if (ReadCount) {
		SPI_DUMP_BUFFER("RD",Read,ReadCount,size);
	}
	
	return TRUE;
}


//--//
//
//	CPOL  == MSK_IDLE
//	NCPHA == MSK_SampleEdge
//
BOOL AT91_SPI_Driver::Xaction_Start( const SPI_CONFIGURATION& Configuration )
{
	UINT32	cs_reg;
	UINT32	cs_sel;
	UINT32	scbr;
	UINT32  dlybs;
	UINT32  dlybct;
	UINT32  cpol;
	UINT32  ncpha;

	AT91_SPI *spi = g_spi[Configuration.SPI_mod];
	//SPI_DEBUG_PRINT("SPI md=%d cs=%d\r\n",Configuration.SPI_mod,Configuration.DeviceCS);

	// calc baudrate
	scbr  = (SYSTEM_PERIPHERAL_CLOCK_HZ) / (Configuration.Clock_RateKHz*1000);
	// limit to min/max
	if (scbr > 255)
		scbr = 255;

	// AT91SAM9261 ERRATA
	// - 41.2.8.7 SPI: Bad Serial Clock Generation on 2nd Chip Select
	// - 41.2.8.5 SPI: Baudrate set to 1
	// - 41.2.8.2 SPI: Bad PDC behavior when CSAAT=1 and SCBR = 1
	// - 41.2.8.1 SPI: Pulse Generation on SPCK
	scbr &= 0xfe;	// make sure scbr is even (and so not 1)
	if (scbr == 0) {
		scbr = 2;	// make sure scbr is not 0
	}

	//--SPI_PRINT_REG(scbr);

	// Delay pre to cs active, pretty limited
	dlybs = (Configuration.CS_Setup_uSecs * SYSTEM_PERIPHERAL_CLOCK_HZ) / 1000000;
	// limit to max
	if (dlybs > 255) {
		dlybs = 255;
	}
	if (dlybs == 0)
		dlybs = 1;		// use a small delay by default

	//--SPI_PRINT_REG(Configuration.CS_Setup_uSecs);

	// Minimum inactive time of CS, not excactly what .NET MF things this is good for
	// dlybct  = (Configuration.CS_Hold_uSecs * SYSTEM_PERIPHERAL_CLOCK_HZ) / (1000000 * 32);
	// limit to max
	// if (dlybct > 255) {
		// dlybct = 255;
	// }
	// if (dlybct == 0)
	holdoff[Configuration.SPI_mod] = Configuration.CS_Hold_uSecs;		

	dlybct = 1;		// use a small delay by default to suppress junk on the line

	//--SPI_PRINT_REG(Configuration.CS_Hold_uSecs);

	//	SPI MODE   MSK_IDLE MSK_SampleEdge		CPOL	NCPHA
	// 	===========================================================
	//		0		0			1				0		1
	//		1		0			0				0		0
	//		2		1			0				1		1
	//		3		1			1				1		0
	if (!Configuration.MSK_IDLE && Configuration.MSK_SampleEdge) {
		// mode 0
		cpol = 0; ncpha = 1;
	} else if (!Configuration.MSK_IDLE && !Configuration.MSK_SampleEdge) {
		// mode 1
		cpol = 0; ncpha = 0;
	} else if (Configuration.MSK_IDLE && !Configuration.MSK_SampleEdge) {
		// mode 2
		cpol = 1; ncpha = 1;
	} else /* if (Configuration.MSK_IDLE && Configuration.MSK_SampleEdge) */ {
		// mode 3
		cpol = 1; ncpha = 0;
	}

	cpol  = ((Configuration.MSK_IDLE) ? AT91_SPI::SPI_CSR_CPOL : 0);
	ncpha = ((Configuration.MSK_SampleEdge ^ Configuration.MSK_IDLE) ? AT91_SPI::SPI_CSR_NCPHA : 0);

	//--SPI_DEBUG_PRINT("MSK_IDLE=%d MSG_SampleEdge=%d cpol=%d ncpha=%d\r\n",
	//				 Configuration.MSK_IDLE ? 1 : 0,
	//				 Configuration.MSK_SampleEdge ? 1 : 0,
	//				 cpol ? 1 : 0,
	//				 ncpha  ? 1 : 0);

	// setup chip select, 8/16 bits per transfer
	cs_reg =  cpol |
			  ncpha |
			 ((dlybs << AT91_SPI::SPI_CSR_DLYBS_SHIFT)&AT91_SPI::SPI_CSR_DLYBS_MASK)| 
			 ((dlybct << AT91_SPI::SPI_CSR_DLYBCT_SHIFT)&AT91_SPI::SPI_CSR_DLYBCT_MASK)|
			 ((scbr << AT91_SPI::SPI_CSR_SCBR_SHIFT)&AT91_SPI::SPI_CSR_SCBR_MASK)| 
			 (Configuration.MD_16bits ? AT91_SPI::SPI_CSR_16BITS : AT91_SPI::SPI_CSR_8BITS);

	//--SPI_PRINT_REG(cs_reg);
	// wait for spi to get ready
	while (is_spi_busy(spi));

	// setup spi controller
	//--SPI_PRINT_REG(Configuration.DeviceCS);
	switch (Configuration.DeviceCS) {
		case AT91_GPIO_Driver::PA3:
			cs_sel = 0;
			spi->SPI_CSR0 = cs_reg;
			break;
		case AT91_GPIO_Driver::PA4:
			cs_sel = 1;
			spi->SPI_CSR1 = cs_reg;
			break;
		case AT91_GPIO_Driver::PA5:
			cs_sel = 2;
			spi->SPI_CSR2 = cs_reg;
			break;
		case AT91_GPIO_Driver::PC14:
			cs_sel = 2;
			spi->SPI_CSR2 = cs_reg;
			break;
		case AT91_GPIO_Driver::PA6:
			cs_sel = 3;
			spi->SPI_CSR3 = cs_reg;
			break;
		case AT91_GPIO_Driver::PC15:
			cs_sel = 3;
			spi->SPI_CSR3 = cs_reg;
			break;
		default:
			SPI_DEBUG_PRINT("UNKNOWN CS, FAIL\r\n");
			return FALSE;
	}

	//--SPI_PRINT_REG(cs_sel);
	// AT91SAM9261 ERRATA
	// - 41.2.8.4 SPI: Chip Select and fixed mode
	spi->SPI_CSR0 = cs_reg;

	spi->SPI_MR = ((pcsmap[cs_sel]<<AT91_SPI::SPI_MR_PCS_SHIFT)&AT91_SPI::SPI_MR_PCS_MASK) | AT91_SPI::SPI_MR_MSTR | AT91_SPI::SPI_MR_MODFDIS;
	//--SPI_PRINT_REG(spi->SPI_MR);
	//--SPI_PRINT_REG(spi->CSR);
	//--SPI_PRINT_REG(spi->SPI_SR);
	cs_reg = spi->SPI_RDR;

    return TRUE;
}


BOOL AT91_SPI_Driver::Xaction_Stop( const SPI_CONFIGURATION& Configuration )
{
	return TRUE;
}


BOOL AT91_SPI_Driver::Xaction_nWrite16_nRead16( SPI_XACTION_16& Transaction )
{
	unsigned index = Transaction.SPI_mod;
	BOOL rval;
	
	if (index > 1) return FALSE;
	
	rval = spi_transfer(g_spi[index],
						Transaction.Write16,
						Transaction.WriteCount,
						Transaction.Read16,
						Transaction.ReadCount,
						Transaction.ReadStartOffset,
						2);
	if (holdoff[index]) 
		HAL_Time_Sleep_MicroSeconds(holdoff[index]);					
	
	return rval;
}

BOOL AT91_SPI_Driver::Xaction_nWrite8_nRead8( SPI_XACTION_8& Transaction )
{
	unsigned index = Transaction.SPI_mod;
	BOOL rval;
	
	if (index > 1) return FALSE;

	rval = spi_transfer(g_spi[index],
						Transaction.Write8,
						Transaction.WriteCount,
						Transaction.Read8,
						Transaction.ReadCount,
						Transaction.ReadStartOffset,
						1);

	if (holdoff[index]) 
		HAL_Time_Sleep_MicroSeconds(holdoff[index]);					
	
	return rval;
}

BOOL AT91_SPI_Driver::nWrite16_nRead16( const SPI_CONFIGURATION& Configuration, UINT16* Write16, INT32 WriteCount, UINT16* Read16, INT32 ReadCount, INT32 ReadStartOffset )
{
	BOOL ok = TRUE;

    // SPI_DEBUG_PRINT("SPI write 16 [%d,%d,%d]\r\n",Configuration.SPI_mod,Configuration.MD_16bits,Configuration.DeviceCS);

	if(Configuration.SPI_mod > AT91_SPI::c_MAX_SPI)
    {
        SPI_DEBUG_PRINT("SPI %d wrong mod\r\n",Configuration.SPI_mod);
        return FALSE;
    }

    if(!g_AT91_SPI_Driver.m_Enabled[Configuration.SPI_mod])
    {
        SPI_DEBUG_PRINT("SPI %d not enabled\r\n",Configuration.SPI_mod);
        return FALSE;
    }

    if(Xaction_Start( Configuration ))
    {
        SPI_XACTION_16 Transaction;

        Transaction.Read16          = Read16;
        Transaction.ReadCount       = ReadCount;
        Transaction.ReadStartOffset = ReadStartOffset;
        Transaction.Write16         = Write16;
        Transaction.WriteCount      = WriteCount;
        Transaction.SPI_mod         = Configuration.SPI_mod;

		// if (Transaction.SPI_mod == 1) msac_dbg_dump("SPI1>:", (void *)Write16, WriteCount, 2);
        ok = Xaction_nWrite16_nRead16( Transaction );
		// if (Transaction.SPI_mod == 1 && ReadCount) msac_dbg_dump("SPI1<:", (void *)Read16, ReadCount, 2);
    }
	else
	{
		SPI_DEBUG_PRINT("XACTION_START_FAILED\r\n");
		return FALSE;
	}

    //--SPI_DEBUG_PRINT("Xaction_nWrite16_nRead16 returned %d\r\n",ok);

    return Xaction_Stop( Configuration ) && ok;
}

BOOL AT91_SPI_Driver::nWrite8_nRead8( const SPI_CONFIGURATION& Configuration, UINT8* Write8, INT32 WriteCount, UINT8* Read8, INT32 ReadCount, INT32 ReadStartOffset )
{
	BOOL ok = TRUE;

    // SPI_DEBUG_PRINT("SPI write 8 [%d,%d,%d]\r\n",Configuration.SPI_mod,Configuration.MD_16bits,Configuration.DeviceCS);

	if(Configuration.SPI_mod > AT91_SPI::c_MAX_SPI)
    {
        SPI_DEBUG_PRINT("SPI %d wrong mod\r\n",Configuration.SPI_mod);
        return FALSE;
    }

    if(!g_AT91_SPI_Driver.m_Enabled[Configuration.SPI_mod])
    {
        SPI_DEBUG_PRINT("SPI %d not enabled\r\n",Configuration.SPI_mod);
        return FALSE;
    }

    if(Xaction_Start( Configuration ))
    {
        SPI_XACTION_8 Transaction;

        Transaction.Read8           = Read8;
        Transaction.ReadCount       = ReadCount;
        Transaction.ReadStartOffset = ReadStartOffset;
        Transaction.Write8          = Write8;
        Transaction.WriteCount      = WriteCount;
        Transaction.SPI_mod         = Configuration.SPI_mod;

        // if (Transaction.SPI_mod == 1) msac_dbg_dump("SPI1>:", (void *)Write8, WriteCount, 1);
        ok = Xaction_nWrite8_nRead8( Transaction );
		// if (Transaction.SPI_mod == 1 && ReadCount) msac_dbg_dump("SPI1<:", (void *)Read8, ReadCount, 1);
    } else {
		SPI_DEBUG_PRINT("XACTION_START_FAILED\r\n");
		return FALSE;
	}

    //--SPI_DEBUG_PRINT("Xaction_nWrite16_nRead16 returned %d\r\n",ok);

	return Xaction_Stop( Configuration ) && ok;
}

void AT91_SPI_Driver::ISR( void* Param )
{
}

